package cn.wp.ewallet.wmps.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.wp.common.core.id.Id;
import cn.wp.common.core.util.BeanUtil;
import cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant;
import cn.wp.ewallet.wallet.model.dto.AccountInfoDto;
import cn.wp.ewallet.wallet.model.entity.AccountInfoDo;
import cn.wp.ewallet.wallet.model.entity.AccountLogDo;
import cn.wp.ewallet.wallet.service.AccountInfoService;
import cn.wp.ewallet.wallet.service.AccountLogService;
import cn.wp.ewallet.wmps.dao.*;
import cn.wp.ewallet.wmps.extension.constant.EwalletWmpsConstant;
import cn.wp.ewallet.wmps.model.entity.*;
import cn.wp.ewallet.wmps.service.ProdOrderRelService;
import cn.wp.ewallet.wmps.service.RegularSettlementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

import static cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant.ACCOUNT_TYPE_AVAILABLE;

@Service
@Slf4j
public class RegularSettlementServiceImpl implements RegularSettlementService {

    @Autowired
    private ProdOrderRelDao prodOrderRelDao;

    @Autowired
    private FixProdOrderRelDao fixProdOrderRelDao;

    @Autowired
    private EarnDetailLogDao earnDetailLogDao;

    @Autowired
    private AccountInfoService accountInfoService;

    @Autowired
    private EarnStatLogDao earnStatLogDao;

    @Autowired
    private ReturnYieldInfoDao returnYieldInfoDao;

    @Autowired
    private AccountLogService accountLogService;

    @Autowired
    private FinaProdInfoDao finaProdInfoDao;

    @Autowired
    private ProdOrderRelService prodOrderRelService;

    @Autowired
    private FixFinaProdInfoDao fixFinaProdInfoDao;


    @Override
    @Transactional
    public void regularSettlement() {
        //读取当前已生效的定期理财产品订单
        List<ProdOrderRelDo> prodOrderRelDoList = prodOrderRelDao.selectProdOrderRels();
        for (int i = 0; i < prodOrderRelDoList.size(); i++) {
            ProdOrderRelDo prodOrderRelDo = prodOrderRelDoList.get(i);
            //读取该订单下的每日利息返回额
            FixProdOrderRelDo fixProdOrderRelDo = fixProdOrderRelDao.selectFixProdOrderRelByProdOrderId(prodOrderRelDo.getProdOrderId());
            if (fixProdOrderRelDo == null) {
                log.info("定期理财产品订单为空 prodOrderId:" + prodOrderRelDo.getProdOrderId());
                continue;
            }
            //获取每日利息
            BigDecimal dailyReturnAmount = fixProdOrderRelDo.getDailyReturnAmount();
            //生成明细记录
            createFixProdOrderRel(prodOrderRelDo, dailyReturnAmount);
            //根据账户id查看钱包地址
            String address = accountInfoService.getAddressByAccountId(prodOrderRelDo.getAccountId());
            //查看收益统计
            EarnStatLogDo earnStatLogDo = earnStatLogDao.selectEarnStatLogByDate(address, yesterdayDateFormartDate(new Date()));
            if (earnStatLogDo != null) {
                //每日待返还利息=上一天的+当日每日利息
                earnStatLogDo.setReturnEarn(earnStatLogDo.getReturnEarn().add(dailyReturnAmount));
                earnStatLogDao.update(earnStatLogDo);
            } else {
                //生成统计收益记录
                earnStatLogDo = getEarnStatLogDo(dailyReturnAmount, address);
            }
            //查看该理财产品的反息周期中的返息
            ReturnYieldInfoDo returnYieldInfoDo = returnYieldInfoDao.selectReturnYieldInfo(prodOrderRelDo.getProdOrderId());
            if (returnYieldInfoDo == null) {
                log.info("该理财产品没有返息周期 prodOrderId：" + prodOrderRelDo.getProdOrderId());
                continue;
            }
            Date returnDate = returnYieldInfoDo.getReturnDate();

            //判断当日是否为返息日
            boolean flag = judgeDateEuqels(new Date(), returnDate);
            if (flag) {
                log.info("当日为返息日 prodOrderId:" + prodOrderRelDo.getProdOrderId());
                //重新设置账户可支配金额,并记录账户变动信息
                accountInfoService.saveAccountChange(address, returnYieldInfoDo.getReturnYield(), EwalletWalletConstant.ACCOUNT_ADD, EwalletWalletConstant.ACCOUNT_CHARGE_CASE_FINANCIAL_INCOME, EwalletWalletConstant.MONEY_TYPE_SHQC, ACCOUNT_TYPE_AVAILABLE);
                //查看整个周期内所有的状态为未返还的收益明细，改为已返还
                List<EarnDetailLogDo> earnDetailLogDos = earnDetailLogDao.selectEarnDetailLogByProIdAndStatus(prodOrderRelDo.getProdOrderId(), EwalletWmpsConstant.RETURN_STATUS_WATT);
                for (int j = 0; j < earnDetailLogDos.size(); j++) {
                    earnDetailLogDos.get(j).setEarnStatus(EwalletWmpsConstant.RETURN_STATUS_ALREADY);
                }
                //将待返回改为已返回
                earnDetailLogDao.updateBabch(earnDetailLogDos);
                //更新返回周期状态为已返还
                updateReturnYiledInfoStatus(returnYieldInfoDo);
                //添加定期理财产品累记收益金额(原来的实际收益+该返息周期应收收益)
                fixProdOrderRelDo.setActualYield(fixProdOrderRelDo.getActualYield().add(returnYieldInfoDo.getReturnYield()));
                fixProdOrderRelDao.updateFixProdOrderRel(fixProdOrderRelDo);
                //待返还数据，定期理财总收益，总收益
                updateTotalAndRretutnEarn(fixProdOrderRelDo, earnStatLogDo);
                //判断当前订单时间是否到期
                boolean prodFlag = judgeDateEuqels(new Date(), fixProdOrderRelDo.getExpTime());
                if (prodFlag) {
                    log.info("理财产品到期 prodOrderId: " + prodOrderRelDo.getProdOrderId());
                    prodOrderRelDo.setOrderStatus(EwalletWmpsConstant.ORDER_STATUS_FINISH);
                    prodOrderRelDo.setStatusTime(new Date());
                    prodOrderRelDao.updateProdOrderRelDo(prodOrderRelDo);
                    //判断是否到期自动续费
                    String isAutoRenewal = fixProdOrderRelDo.getIsAutoRenewal();
                    //自动续费
                    if (EwalletWmpsConstant.IS_EARLY_REDEMP.equals(isAutoRenewal)) {
                        FinaProdInfoDo finaProdInfoDo = finaProdInfoDao.selectFinaProdInfoById(prodOrderRelDo.getFinaProdId());
                        if (finaProdInfoDo.getStatus().equals(EwalletWmpsConstant.FINA_PROD_STATUS_UPPER)) {
                            //修改定期理财产品订单状态
                            ProdOrderRelDo newProdOrderRelDo = newProdOrderRelDo(prodOrderRelDo, fixProdOrderRelDo);

                            BigDecimal yieldRate = finaProdInfoDo.getYieldRate();
                            BigDecimal purchAmountLower = newProdOrderRelDo.getPurchAmount();
                            //预计总收益
                            BigDecimal earnAmount = purchAmountLower.multiply(yieldRate);
                            FixFinaProdInfoDo fixFinaProdInfoDo = fixFinaProdInfoDao.selectfixFinaProdInfo(newProdOrderRelDo.getFinaProdId());
                            BigDecimal finaProdCycle = fixFinaProdInfoDo.getFinaProdCycle();

                            int returnRateCycle;
                            if (EwalletWmpsConstant.RETURN_RATE_CYCLE_QUARTER.equals(finaProdInfoDo.getReturnRateCycle())) {
                                returnRateCycle = 90;
                            } else {
                                returnRateCycle = 1;
                            }
                            LocalDate effDate = LocalDate.now().plusDays(1);
                            LocalDate expDate = effDate.plusDays(finaProdCycle.longValue());
                            log.info("finaProdCycle " + finaProdCycle + "expDate" + expDate + "finaProdCycle.longValue()" + finaProdCycle.longValue());
                            saveFixProdOrderRel(EwalletWmpsConstant.IS_EARLY_REDEMP, earnAmount, dailyReturnAmount, expDate, newProdOrderRelDo);
                            //生成返息周期表
                            prodOrderRelService.getReturnYieldInfoDo(finaProdCycle, returnRateCycle, dailyReturnAmount, effDate, expDate, prodOrderRelDo);
                        }
                    } else {
                        //不续费,回退本金
                        updateEffDateProOrderInfo(prodOrderRelDo);

                        //保存可用账户资金变动信息
                        accountInfoService.saveAccountChange(address, prodOrderRelDo.getPurchAmount(), EwalletWalletConstant.ACCOUNT_ADD, EwalletWalletConstant.ACCOUNT_CHARGE_CASE_FINANCIAL_INCOME, EwalletWalletConstant.MONEY_TYPE_SHQC, EwalletWmpsConstant.ACCOUNT_TYPE_AVAILABLE);
                        //保存理财账户资金变动信息
                        accountInfoService.saveAccountChange(address, prodOrderRelDo.getPurchAmount(), EwalletWalletConstant.ACCOUNT_SUBTRACT, EwalletWalletConstant.ACCOUNT_CHARGE_CASE_FINANCIAL_INCOME, EwalletWalletConstant.MONEY_TYPE_SHQC, EwalletWmpsConstant.ACCOUNT_TYPE_FINA);
                    }
                } else {
                    log.info("理财产品未到期 prodOrderId: " + prodOrderRelDo.getProdOrderId());
                    updateEffDateProOrderInfo(prodOrderRelDo);
                }

            } else {
                log.info("当日不是返息日 prodOrderId: " + prodOrderRelDo.getProdOrderId());
                updateEffDateProOrderInfo(prodOrderRelDo);
            }
        }
    }

    private EarnStatLogDo getEarnStatLogDo(BigDecimal dailyReturnAmount, String address) {
        EarnStatLogDo earnStatLogDo = new EarnStatLogDo();
        //查询昨日的收益统计
        EarnStatLogDo yesterdayEarnStatLogDo = earnStatLogDao.selectEarnStatLogByDate(address, offererdayDate(new Date()));
        if (yesterdayEarnStatLogDo == null) {
            earnStatLogDo.setStatDate(DateUtil.offsetDay(new Date(), -1));
            earnStatLogDo.setWalletAddress(address);
            earnStatLogDo.setTtyTotalEarn(new BigDecimal(0));
            earnStatLogDo.setFinaTotalEarn(new BigDecimal(0));
            earnStatLogDo.setTotalEarn(new BigDecimal(0));
            earnStatLogDo.setReturnEarn(dailyReturnAmount);
        } else {
            earnStatLogDo.setStatDate(DateUtil.offsetDay(new Date(), -1));
            earnStatLogDo.setWalletAddress(address);
            earnStatLogDo.setReturnEarn(yesterdayEarnStatLogDo.getReturnEarn().add(dailyReturnAmount));
            earnStatLogDo.setTtyTotalEarn(yesterdayEarnStatLogDo.getTtyTotalEarn());
            earnStatLogDo.setFinaTotalEarn(yesterdayEarnStatLogDo.getFinaTotalEarn());
            earnStatLogDo.setTotalEarn(yesterdayEarnStatLogDo.getTotalEarn());
        }
        earnStatLogDao.insert(earnStatLogDo);
        return earnStatLogDo;
    }

    private void createAccountLog(ProdOrderRelDo prodOrderRelDo, AccountInfoDo accountInfoDo, AccountInfoDo accountInfoDo1) {
        AccountLogDo accountLogDo2 = new AccountLogDo();
        accountLogDo2.setLogId(Id.gen());
        accountLogDo2.setLogTime(new Date());
        accountLogDo2.setAccountId(accountInfoDo.getAccountId());
        accountLogDo2.setBeginMoneyAmount(accountInfoDo1.getMoneyAmount().add(prodOrderRelDo.getPurchAmount()));
        accountLogDo2.setEndMoneyAmount(accountInfoDo1.getMoneyAmount());
        accountLogDo2.setBalanceAmount(accountLogDo2.getEndMoneyAmount().subtract(accountLogDo2.getBeginMoneyAmount()));
        accountLogDo2.setChangeCause("9000");
        accountLogService.addAccountLogDo(accountLogDo2);
    }

    private void createAccountLog(ProdOrderRelDo prodOrderRelDo, AccountInfoDo accountInfoDo) {
        AccountLogDo accountLogDo1 = new AccountLogDo();
        accountLogDo1.setLogId(Id.gen());
        accountLogDo1.setAccountId(accountInfoDo.getAccountId());
        accountLogDo1.setLogTime(new Date());
        accountLogDo1.setBeginMoneyAmount(accountInfoDo.getMoneyAmount().subtract(prodOrderRelDo.getPurchAmount()));
        accountLogDo1.setEndMoneyAmount(accountInfoDo.getMoneyAmount());
        accountLogDo1.setBalanceAmount(prodOrderRelDo.getPurchAmount());
        accountLogDo1.setChangeCause("9000");
        accountLogService.addAccountLogDo(accountLogDo1);
    }

    private void createAccountLog(ReturnYieldInfoDo returnYieldInfoDo, AccountInfoDo accountInfoDo) {
        AccountLogDo accountLogDo = new AccountLogDo();
        accountLogDo.setLogId(Id.gen());
        accountLogDo.setBeginMoneyAmount(accountInfoDo.getMoneyAmount().subtract(returnYieldInfoDo.getReturnYield()));
        accountLogDo.setEndMoneyAmount(accountInfoDo.getMoneyAmount());
        accountLogDo.setBalanceAmount(returnYieldInfoDo.getReturnYield());
        accountLogDo.setAccountId(accountInfoDo.getAccountId());
        accountLogDo.setLogTime(new Date());
        accountLogDo.setChangeCause("6000");
        accountLogService.addAccountLogDo(accountLogDo);
    }

    private void updateEffDateProOrderInfo(ProdOrderRelDo prodOrderRelDo) {
        boolean effDateFlag = judgeDateEuqels(new Date(), prodOrderRelDo.getEffDate());
        //存在当前生效日期为当前的订单
        if (effDateFlag) {
            prodOrderRelDo.setOrderStatus(EwalletWmpsConstant.ORDER_STATUS_BEARING);
            prodOrderRelDao.updateProdOrderRelDo(prodOrderRelDo);
        }
    }

    /***
     *
     * 待返还数据，定期理财总收益，总收益
     * @param fixProdOrderRelDo
     * @param earnStatLogDo
     */

    private void updateTotalAndRretutnEarn(FixProdOrderRelDo fixProdOrderRelDo, EarnStatLogDo earnStatLogDo) {
        //定期理财总收益=原来的+到期的总利息
        earnStatLogDo.setFinaTotalEarn(earnStatLogDo.getFinaTotalEarn().add(fixProdOrderRelDo.getActualYield()));
        //待返回的收益=原来的待返回收益-到期应收收益
        earnStatLogDo.setReturnEarn(earnStatLogDo.getReturnEarn().subtract(fixProdOrderRelDo.getActualYield()));
        //总收益=原来的总收益+定理理财返回的收益
        earnStatLogDo.setTotalEarn(earnStatLogDo.getTotalEarn().add(fixProdOrderRelDo.getActualYield()));
        earnStatLogDao.update(earnStatLogDo);
    }

    private void updateReturnYiledInfoStatus(ReturnYieldInfoDo returnYieldInfoDo) {
        returnYieldInfoDo.setReturnStatus(EwalletWmpsConstant.RETURN_STATUS_ALREADY);
        returnYieldInfoDao.update(returnYieldInfoDo);
    }

    /**
     * 重新设置账户可支配金额
     *
     * @param address
     * @param returnYieldInfoDo
     * @return
     */

    private AccountInfoDo updateMountAmount(String address, ReturnYieldInfoDo returnYieldInfoDo) {
        AccountInfoDto accountInfoDto = accountInfoService.selectAccountByWalletAddMoneyTypeId(address, EwalletWalletConstant.MONEY_TYPE_SHQC, EwalletWmpsConstant.ACCOUNT_TYPE_AVAILABLE);
        AccountInfoDo accountInfoDo = BeanUtil.copyProperties(accountInfoDto, AccountInfoDo.class);
        //更改账户可支配金额
        accountInfoDo.setMoneyAmount(accountInfoDo.getMoneyAmount().add(returnYieldInfoDo.getReturnYield()));
        accountInfoService.update(BeanUtil.copyProperties(accountInfoDo, AccountInfoDto.class));
        return accountInfoDo;
    }

    /**
     * 生成收益明细
     *
     * @param prodOrderRelDo
     * @param dailyReturnAmount
     */
    private void createFixProdOrderRel(ProdOrderRelDo prodOrderRelDo, BigDecimal dailyReturnAmount) {
        EarnDetailLogDo earnDetailLogDo = new EarnDetailLogDo();
        earnDetailLogDo.setEarnDetailLogId(Id.gen());
        earnDetailLogDo.setEarnStatus(EwalletWmpsConstant.RETURN_STATUS_WATT);
        earnDetailLogDo.setCreateTime(new Date());
        String addressByAccountId = accountInfoService.getAddressByAccountId(prodOrderRelDo.getAccountId());
        AccountInfoDto accountInfoDto = accountInfoService.selectAccountByWalletAddMoneyTypeId(addressByAccountId, EwalletWalletConstant.MONEY_TYPE_SHQC, ACCOUNT_TYPE_AVAILABLE);
        earnDetailLogDo.setAccountId(accountInfoDto.getAccountId());
        earnDetailLogDo.setProdOrderId(prodOrderRelDo.getProdOrderId());
        earnDetailLogDo.setDailyEarn(dailyReturnAmount);
        earnDetailLogDo.setEarnDate(DateUtil.offsetDay(new Date(), -1));
        earnDetailLogDao.insert(earnDetailLogDo);
    }


    public boolean judgeDateEuqels(Date date1, Date date2) {
        String dateFormatDate1 = DateUtil.formatDate(date1);
        String dateFormatDate2 = DateUtil.formatDate(date2);
        return dateFormatDate1.equals(dateFormatDate2);
    }

    public String yesterdayDateFormartDate(Date date) {

        return DateUtil.formatDate(DateUtil.offsetDay(date, -1));
    }

    public String offererdayDate(Date date) {
        return DateUtil.formatDate(DateUtil.offsetDay(date, -2));
    }


    public ProdOrderRelDo newProdOrderRelDo(ProdOrderRelDo prodOrderRelDo, FixProdOrderRelDo fixProdOrderRelDo) {
        ProdOrderRelDo newProdOrderRelDo = new ProdOrderRelDo();
        newProdOrderRelDo.setProdOrderId(Id.gen());
        newProdOrderRelDo.setAccountId(prodOrderRelDo.getAccountId());
        newProdOrderRelDo.setOrderStatus(EwalletWmpsConstant.ORDER_STATUS_BEARING);
        newProdOrderRelDo.setFinaProdId(prodOrderRelDo.getFinaProdId());
        newProdOrderRelDo.setPurchAmount(prodOrderRelDo.getPurchAmount());
        newProdOrderRelDo.setEffDate(new Date());
        newProdOrderRelDo.setPurchTime(fixProdOrderRelDo.getExpTime());
        newProdOrderRelDo.setStatusTime(new Date());
        prodOrderRelDao.insertProdOrderRel(newProdOrderRelDo);
        return newProdOrderRelDo;
    }

    private void saveFixProdOrderRel(String isAutoRenewal, BigDecimal earnAmount, BigDecimal dailyReturnAmount, LocalDate expDate, ProdOrderRelDo prodOrderRelDo) {
        FixProdOrderRelDo fixProdOrderRelDo = new FixProdOrderRelDo();
        fixProdOrderRelDo.setProdOrderId(prodOrderRelDo.getProdOrderId());
        fixProdOrderRelDo.setRecYield(earnAmount);
        fixProdOrderRelDo.setDailyReturnAmount(dailyReturnAmount);
        fixProdOrderRelDo.setActualYield(new BigDecimal(0));
        fixProdOrderRelDo.setExpTime(Date.from(expDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
        fixProdOrderRelDo.setIsAutoRenewal(isAutoRenewal);
        fixProdOrderRelDao.saveFixProdOrderRel(fixProdOrderRelDo);
    }

}
